package es.us.idinfor.rmplugin.operator;

import java.util.List;

import com.rapidminer.example.ExampleSet;

import com.rapidminer.operator.Model;
import com.rapidminer.operator.Operator;
import com.rapidminer.operator.OperatorCapability;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.OperatorException;
import com.rapidminer.operator.annotation.ResourceConsumptionEstimator;
import com.rapidminer.operator.learner.AbstractLearner;
import com.rapidminer.operator.learner.CapabilityProvider;
import com.rapidminer.operator.learner.Learner;

import com.rapidminer.parameter.ParameterType;
import com.rapidminer.parameter.ParameterTypeCategory;
import com.rapidminer.parameter.ParameterTypeDouble;

import com.rapidminer.tools.OperatorResourceConsumptionHandler;

import es.us.idinfor.rmplugin.example.ExamplesFactory;
import es.us.idinfor.rmplugin.model.LAMDACenteredBinomialModel;
import es.us.idinfor.rmplugin.model.LAMDAFuzzyBinomialModel;
import es.us.idinfor.rmplugin.model.LAMDAGaussianModel;
import es.us.idinfor.rmplugin.util.Utils;

/**
 * <p>This operator returns the classification model of the dataset.</p>
 * 
 * <p>It is based on LAMDA algorith.</p>
 * 
 * @author Miguel Angel Alvarez de la Concepcion
 * @version 1.0
 */
public class LAMDALearner extends AbstractLearner {
	
	/**
	 * The parameter name for the function type.
	 */
	public static final String PARAMETER_FUNCTION_TYPE = "function_type";
	private static final String[] FUNCTION_TYPES = new String[] {"gaussian", "fuzzy_binomial", "centered_binomial"};
	
	/**
	 * The parameter name for the alpha value.
	 */
	public static final String PARAMETER_ALPHA = "alpha";
	
	/**
	 * Default constructor.
	 * 
	 * @param description The description of the operator.
	 */
	public LAMDALearner(OperatorDescription description) {
        super(description);
    }

	/**
	 * @see Learner#learn(ExampleSet)
	 */
	public Model learn(ExampleSet exampleSet) throws OperatorException {
		if(exampleSet.getAttributes().getLabel() == null) {
			logError("There is not a label attribute.");
			throw new OperatorException("LAMDA operator must receive an example set with a label.");
		}
		else if(exampleSet.getAttributes().getLabel().isNumerical()) {
			logError("The label attribute is not nominal.");
			throw new OperatorException("LAMDA Selection operator must receive an example set with a nominal label.");
		}
		else if(exampleSet.getAttributes().getLabel().getMapping().size() != 2) {
			logError("The number of classes is not equals to two.");
			throw new OperatorException("LAMDA operator must receive a label with only two differents values.");
		}
		else {
			try {
				Model res = null;
				ExampleSet copy = ExamplesFactory.getCopy(exampleSet);
				Utils.normalize(copy);
				
				if(getParameterAsString(PARAMETER_FUNCTION_TYPE).equals("gaussian")) {
					res = new LAMDAGaussianModel(copy, getParameterAsDouble(PARAMETER_ALPHA));
				}
				else if(getParameterAsString(PARAMETER_FUNCTION_TYPE).equals("fuzzy_binomial")) {
					res = new LAMDAFuzzyBinomialModel(copy, getParameterAsDouble(PARAMETER_ALPHA));
				}
				else if(getParameterAsString(PARAMETER_FUNCTION_TYPE).equals("centered_binomial")) {
					res = new LAMDACenteredBinomialModel(copy, getParameterAsDouble(PARAMETER_ALPHA));
				}
				else {
					logError("The type of the function is unknown.");
					throw new OperatorException("LAMDA operator does not recognize the type of the function.");
				}
				
				copy = null;
				
				return res;
			} catch(OperatorException e) {
				throw new OperatorException("LAMDA operator has had an error with a model.");
			}
		}
	}

	/**
	 * @see CapabilityProvider#supportsCapability(OperatorCapability)
	 */
	public boolean supportsCapability(OperatorCapability capability) {
		switch (capability) {
	        case NUMERICAL_ATTRIBUTES:
	        case POLYNOMINAL_LABEL:
	        case BINOMINAL_LABEL:
	            return true;
	        default:
	            return false;
        }
	}
	
	/**
	 * @see Operator#getParameterTypes()
	 */
	public List<ParameterType> getParameterTypes() {
		List<ParameterType> types = super.getParameterTypes();
		ParameterType type = null;
		
		type = new ParameterTypeCategory(PARAMETER_FUNCTION_TYPE, "The type of the function.", FUNCTION_TYPES, 0, false);
		types.add(type);
		
		type = new ParameterTypeDouble(PARAMETER_ALPHA, "The alpha value.", 0.0d, 1.0d, 0.5d, false);
		types.add(type);
		
		type = null;
		
		return types;
	}
	
	/**
	 * @see Operator#getResourceConsumptionEstimator()
	 */
	public ResourceConsumptionEstimator getResourceConsumptionEstimator() {
		return OperatorResourceConsumptionHandler.getResourceConsumptionEstimator(getExampleSetInputPort(), LAMDALearner.class, null);
	}
}
